/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package noyau;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JTextArea;

/**
 *
 * @author Hoang
 */
public class LiaisonCapteur implements Runnable {
    private Socket sockComCapteur; //Socket permettant la communication avec les capteurs
    private DataInputStream in;    //Flux permettant de recevoir des donnees
    private PrintWriter out;
    private DataOutputStream outServeur;
    private String msgRecu;       //String contenant le msg en hexa provenant d'un capteur
    private ArrayList<Capteur> ListeCapteurs;  //Liste de capteur a surveiller
    private LiaisonServeur ls;
    private String adrCapteur;
    private int portCapteur;
    private JTextArea textMsg;

    /**
     * Constructeur de la liaison. Ne pas initialiser la connexion.
     * @param adrCapteur Adresse du récepteur
     * @param portCapteur Port du récepteur
     * @param ListeCapteurs Liste des capteurs.
     */
    public LiaisonCapteur(ArrayList<Capteur> ListeCapteurs, String adrCapteur, 
            int portCapteur, JTextArea textMsg, DataOutputStream out) {
        this.ListeCapteurs = ListeCapteurs;
        this.adrCapteur = adrCapteur;
        this.portCapteur = portCapteur;
        this.textMsg = textMsg;
        this.outServeur = out;
    }

    public ArrayList<Capteur> getListeCapteurs() {
        return ListeCapteurs;
    }
    
    /**
     * Mode d'emploi :
     * A utiliser pour fournir le contenu des instructions de la tache
     */
    @Override
    public void run() {
        Connect();

        while(true)
        {
            GetMessage();
            IdentifierCapteurEtAnalyser();
        }
    }

    /**
     * Mode d'emploi :
     * Methode a utiliser pour initialiser la communication avec les capteurs
     */
    public void Connect(){
        try {
            // Connexion au serveur contenant les messages des capteurs
            //sockComCapteur = new Socket ("127.0.0.1",5000);
            sockComCapteur = new Socket (this.adrCapteur,this.portCapteur);
            System.out.println("Connexion établie avec le serveur");

            // Creation des flux pour pouvoir communiquer avec le serveur
            in = new DataInputStream(sockComCapteur.getInputStream());
            System.out.println("Flux cree");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * Mode d'emploi :
     * Methode à utiliser pour recuperer les messages provenant des capteurs
     */
    public void GetMessage(){
        try {
         System.out.println("Attente de reception de message");
         byte[] buff = new byte[28];
         int ok  = in.read(buff);
         msgRecu = byteArrayToHexString(buff);

         System.out.println(msgRecu);

        }
        catch (Exception e)
                {
                e.printStackTrace();
                }

    }

    /**
     * Mode d'emploi :
     * Methode à utilisder pour couper la communication avec les capteurs
     */
    public void Deconnexion (){
        try {
         sockComCapteur.close();
        }
         catch (Exception e)
            {
            e.printStackTrace();
            }
    }
    
    /**
     * Mode d'emploi :
     * Recherche si le capteur entré en parametre fait partit de la liste des
     * capteurs a surveiller
     */
    public void IdentifierCapteurEtAnalyser(){        
        String id = msgRecu.substring(16, 24);
        Capteur.TYPE type;

        //System.out.println("L'identifiant du capteur du message est : "+id);

        for (int i=0;i<ListeCapteurs.size();i++)
        {
            if (id.equals(ListeCapteurs.get(i).getId()))
            {
                System.out.println("L'identifiant du capteur du message est : "+id);
                type = ListeCapteurs.get(i).getType();
                
                if (type == Capteur.TYPE.CAP_TEMP_EN)
                {
                    System.out.print(msgRecu+ "    ");
                    System.out.println("Capteur de temperature");
                    AnalyserMesureTemperature(i);
                }
                else if (type == Capteur.TYPE.CAP_LUM_PRE_EN)
                {
                    System.out.print(msgRecu+ "    ");
                    System.out.println("Capteur de presence et de lumiere");
                    AnalyserMesurePresenceLuminosite(i);
                }
                else if (type == Capteur.TYPE.CAP_CONTACT_EN)
                {
                    System.out.print(msgRecu+ "    ");
                    System.out.println("Capteur de Contact");
                    AnalyseMesureContact(i);
                }

                else if (type == Capteur.TYPE.CAP_BOUTON_EN)
                {
                    System.out.print(msgRecu+ "    ");
                    System.out.println("Interrupteur declenche");
                    AnalyseInterrupteur(i);
                }
                else
                {
                    //System.out.println("Capteur inutile");
                }
            }
        }
    }

    /**
     * Mode d'emploi :
     * Methode utilise pour determiner l'etat des boutons de l'interrupteur
     * Pour cela on s'interresse au DATABYTE3 et plus particulierement au bit 7 à 5 
     * Pour simplifier l'etude des valeurs on s'interresse au 1er octet du databyte3
     * S'il vaut 0 le bouton a ete relache
     * @param id 
     */
    public void AnalyseInterrupteur(int id) {
        String bouton = msgRecu.substring(8,9);
        int etatInterrupteur = 0;
        System.out.println("Valeur hexa interrupteur : "+ bouton);
        int boutonAppuye = Integer.parseInt(bouton,16);
        switch (boutonAppuye)
        {
            case 0 : etatInterrupteur = 0;
                     System.out.println(etatInterrupteur);
                     break;

            case 1 : etatInterrupteur = 1;
                     System.out.println(etatInterrupteur);
                     break;
                             
            case 3 : etatInterrupteur = 2;
                     System.out.println(etatInterrupteur);
                     break;
                     
            case 5 : etatInterrupteur = 3;
                     System.out.println(etatInterrupteur);
                     break;

            case 7 : etatInterrupteur = 4;
                     System.out.println(etatInterrupteur);
                     break;
        }
        
        
        if (etatInterrupteur != 0) {
            ListeCapteurs.get(id).setBoutonAppuye(etatInterrupteur);
            ListeCapteurs.get(id).setEtatBouton(1);
        } else
            ListeCapteurs.get(id).setEtatBouton(0);
        this.EnvoyerMsg(ListeCapteurs.get(id));
        this.MAJText();
    }
    
    /**
     * Mode d'emploi :
     * Methode utilisée pour relever la valeur d'une mesure provenant d'un capteur de présence et de luminosite
     * ON s'interesse au DATA_BYTE 2 pour la luminosité et au bit 1 du DATA_BYTE 0 pour la presence
     * On convertit le string contenant la valeur de la mesure (en hexa) en entier
     * Dans le cas de la luminosite on multiplie la valeur obtenue par 40/250 (rapport qui varie selon le type)
     * Sinon pour la presence on convertit l'entier en binaire et on etudie la valeur du bit 1
     * @param id 
     */
    public void AnalyserMesurePresenceLuminosite(int id) {
        String mesureLumiosite = msgRecu.substring(10,12); //DB2
        String mesurePresence = msgRecu.substring(14, 16); //DB0

         // Mesure de la luminosité

        System.out.print("Valeur en hexa du DB2 pour la luminosite : "+ mesureLumiosite + " ");
        int valeurL = Integer.parseInt(mesureLumiosite,16);
        float luminosite = valeurL*510/255;
        System.out.println("Valeur de la luminosité : "+luminosite);


        // Mesure de la presence

        System.out.print("Valeur en hexa du DB0 pour la presence : "+ mesurePresence + "   ");
        int valeurP = Integer.parseInt(mesurePresence,16);
        String donnee = Integer.toBinaryString(valeurP);
        System.out.print("Valeur en binaire du DB0 : "+donnee+ " ");
        int bit1 = donnee.length()-2;

        if (donnee.charAt(bit1) == '1')
        {
            ListeCapteurs.get(id).setPresence(false);
            System.out.println("Aucune presence detectee");
        }
        else
        {
            ListeCapteurs.get(id).setPresence(true);
            System.out.println("Presence detectee ");
        }
        ListeCapteurs.get(id).setLuminosite(luminosite);
        this.EnvoyerMsg(ListeCapteurs.get(id));
        this.MAJText();
    }

    /**
     * Mode d'emploi :
     * Methode utilisée pour verfier l'etat du capteur de contact.
     * Pour cela on s'interesse au bit 0 du DATA_BYTE 0
     * Si il vaut 0 le contact est ferme si il vaut 1 le contact est ouvert
     * @param id 
     */
    public void AnalyseMesureContact(int id) {

        String mesure = msgRecu.substring(14,16);
        System.out.print("Valeur du DB0 pour le contact en hexa : "+ mesure+ "    ");

        int valeurD = Integer.parseInt(mesure,16);
        System.out.print("Valeur du DB0 en decimal : "+ valeurD+"   ");

        String donnee;
        donnee = Integer.toBinaryString(valeurD);
        System.out.println("Valeur mesure en binaire : "+ donnee+"  ");

        int bit0 = donnee.length()-1;

        if (donnee.charAt(bit0)=='0')
        {
            System.out.println("Contact ouvert");
            ListeCapteurs.get(id).setContactOuvert(true);
        }
        else
        {
            System.out.println("Contact ferme");
            ListeCapteurs.get(id).setContactOuvert(false);
        }
        
        this.EnvoyerMsg(ListeCapteurs.get(id));
        this.MAJText();
    }
    
    /**
     * Mode d'emploi :
     * Methode utilisée pour mesurer la valeur d'une mesure provenant d'un capteur de température
     * On ne s'interesse qu'au DATA_BYTE 1
     * On convertit le string contenant la valeur de la mesure (en hexa) en entier
     * L'entier récupéré correspondra à la température en degré celcius
     * @param id 
     */
    public void AnalyserMesureTemperature(int id) {
        String mesure = msgRecu.substring(12,14);
        System.out.println("Valeur mesure en hexa : "+ mesure);

        int valeur = Integer.parseInt(mesure, 16);
        System.out.println("Valeur mesure en decimal : "+ valeur);

        double pente = -0.1568;

        double temperature1 = valeur*pente+40;
        System.out.println("Valeur mesure en degre 1: "+ temperature1);

        ListeCapteurs.get(id).setTemperature(temperature1);
        this.EnvoyerMsg(ListeCapteurs.get(id));
        this.MAJText();
    }
    
    /**
     * Methode utilisée pour convertir un tableau de byte en string
     * On parcours le tableau de byte et on convertit en ASCII la valeur de 
     * chaque case du tableau afin d'avoir le resultat en hexadecimal
     * @param bArray Message à Convertir
     * @return Contenu du message
     */
    static String byteArrayToHexString(byte[] bArray){
    /* Mode d'emploi :
     * Methode utilisée pour convertir un tableau de byte en string
     * On parcours le tableau de byte et on convertit en ASCII la valeur de chaque case du tableau
     * afin d'avoir le resultat en hexadecimal
     */
        StringBuffer buffer = new StringBuffer();
        int i =0;
        while(i<bArray.length) {
              buffer.append((char)bArray[i]);
              buffer.append((char)bArray[i+1]);
              //buffer.append(" ");
              i = i+2;
        }
        return buffer.toString();
    }
    
    public boolean EnleverCapteur(String id){
        boolean verif = false;
        List<Capteur> temp = Collections.synchronizedList(ListeCapteurs);
        synchronized (temp){
            Iterator<Capteur> c = temp.iterator();
            while (c.hasNext())
            {
                if (c.next().getId().equals(id)){
                    c.remove();
                    verif = true;
                }
            }
        }
        return verif;
    }
    
    public void AllumerLumiere(String noActionneur){
        try{
            System.out.println("Actionner bouton B");
            String msgEnvoie = "A55A6B0550000000FF9F1E0"+noActionneur+"1004";
            out = new PrintWriter(sockComCapteur.getOutputStream());
            out.print(msgEnvoie);
            out.flush();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void EteindreLumiere(String noActionneur) {
        try {
            System.out.println("Eteindre bouton B");
            String msgEnvoie = "A55A6B0570000000FF9F1E0"+noActionneur +"1004";
            out = new PrintWriter(sockComCapteur.getOutputStream());
            out.print(msgEnvoie);
            out.flush();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void EnvoyerMsg(Capteur c){
        try {
            switch (c.getType())
            {
                case CAP_TEMP_EN:
                    outServeur.writeUTF(c.getId()+"$"+(int)(c.getTemperature()) + "%\r\n");
                    outServeur.flush();
                    break;
                case CAP_LUM_PRE_EN:
                    outServeur.writeUTF(c.getId()+"$"+(int)(c.getLuminosite()) + "%\r\n");
                    outServeur.flush();
                    break;
                case CAP_CONTACT_EN:
                    outServeur.writeUTF(c.getId()+"$"+(c.getContactOuvert()?1:0) + "%\r\n");
                    outServeur.flush();
                    break;
                case CAP_BOUTON_EN:
                    outServeur.writeUTF(c.getId()+"$"+ 
                            String.valueOf(c.getBoutonAppuye()) +
                            String.valueOf(c.getEtatBouton()) + "%\r\n");
                    outServeur.flush();
                    break;
            }
        } catch (IOException ex) {
            Logger.getLogger(LiaisonCapteur.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void MAJText() {
        textMsg.setText("");
        for (Capteur c:ListeCapteurs){
            switch (c.getType())
            {
                case CAP_TEMP_EN:
                    textMsg.append(c.getId()+"  "+Double.toString(c.getTemperature())+"\r\n");
                    break;
                case CAP_LUM_PRE_EN:
                    textMsg.append(c.getId()+"  "+Float.toString(c.getLuminosite())
                            +Boolean.toString(c.getPresence())+"\r\n");
                    break;
                case CAP_CONTACT_EN:
                    textMsg.append(c.getId()+"  "+Boolean.toString(c.getContactOuvert())+"\r\n");
                    break;
                case CAP_BOUTON_EN:
                    textMsg.append(c.getId()+"  "+c.getBoutonAppuye()+
                            ((c.getEtatBouton()==0)?" relâché":" appuyé") + "\r\n");
                    break;
            }
        }
    }
}
